Completed
Push — master ( 3f33f1...179d8a )
by Ruben de
01:12
created

BtccomConverter.convertTxs   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
nc 1
dl 0
loc 12
rs 9.4285
nop 1

2 Functions

Rating   Name   Duplication   Size   Complexity  
A 0 1 1
A 0 4 1
1
var Wallet = require('./wallet');
2
var blocktrail = require('./blocktrail');
3
var bitcoinJS = require('bitcoinjs-lib');
4
5
var BtccomConverter = function(network, useNewCashAddr) {
6
    this.network = network;
7
    this.useNewCashAddr = useNewCashAddr;
8
};
9
10
function getAddressScriptInfo(address, network, useNewCashAddr) {
11
    var addressScriptInfo;
12
13
    try {
14
        addressScriptInfo = bitcoinJS.address.toOutputScript(address, network, useNewCashAddr);
15
    } catch (e) {
16
        addressScriptInfo = null;
17
    }
18
    return addressScriptInfo;
19
}
20
21
function getScriptAsm(chunks) {
22
    var scriptAsm;
23
24
    try {
25
        scriptAsm = bitcoinJS.script.toASM(chunks);
26
    } catch (e) {
27
        scriptAsm = null;
28
    }
29
    return scriptAsm;
30
}
31
32
function prettifyAsm(asm) {
33
    if (!asm) {
34
        return asm;
35
    }
36
37
    return asm.replace(/^0 /, "OP_0 ");
38
}
39
40
function getType(script) {
41
    var type;
42
43
    try {
44
        type = bitcoinJS.script.classifyOutput(script);
45
    } catch (e) {
46
        type = null;
47
    }
48
    return type;
49
}
50
51
function getBase58AddressHash160(address, network, useNewCashAddr) {
52
    var addressInfo;
53
    try {
54
        addressInfo = Wallet.getAddressAndType(address, network, useNewCashAddr);
55
    } catch (e) {
56
        return null;
57
    }
58
59
    if (addressInfo.type === "base58") {
60
        return addressInfo.decoded.hash;
61
    } else if (addressInfo.type === "bech32") {
62
        if (addressInfo.data.length === 20) {
63
            return addressInfo.decoded.hash;
64
        }
65
        return null;
66
    } else if (addressInfo.type === "") {
67
        return addressInfo.decoded.hash;
68
    }
69
70
    return null;
71
}
72
73
function convertBtccomOutputScriptType(scriptType) {
74
    switch (scriptType) {
75
        case "P2PKH_PUBKEY":
76
            return "pubkey";
77
        case "P2PKH":
78
            return "pubkeyhash";
79
        case "P2SH":
80
            return "scripthash";
81
        case "P2WSH_V0":
82
            return "witnessscripthash";
83
        case "P2WPKH":
84
            return "witnesspubkeyhash";
85
        case "NULL_DATA":
86
            return "op_return";
87
        case "coinbase":
88
            return "coinbase";
89
        default:
90
            throw new Error("Not implemented yet, script type: " + scriptType);
91
    }
92
}
93
94
function utcTimestampToISODateStr(time) {
95
    return (new Date(time * 1000)).toISOString().replace(/\.000Z$/, '+0000');
96
}
97
98
function flattenAddresses(addrs) {
99
    if (!addrs) {
100
        return addrs;
101
    } else if (addrs.length === 1) {
102
        return addrs[0];
103
    } else {
104
        return addrs;
105
    }
106
}
107
108
function convertBtccomTxToBlocktrail(tx) {
109
    /* jshint -W071, -W074 */
110
    var data = {};
111
112
    data.size = tx.vsize;
113
    data.hash = tx.hash;
114
    data.block_height = tx.block_height;
115
    data.time =
116
    data.block_time = utcTimestampToISODateStr(tx.block_time);
117
    data.block_hash = tx.block_hash;
118
    data.confirmations = tx.confirmations;
119
    data.is_coinbase = tx.is_coinbase;
120
121
    var totalInputValue;
122
    if (data.is_coinbase) {
123
        totalInputValue = tx.outputs[0].value - tx.fee;
124
    } else {
125
        totalInputValue = tx.inputs_value;
126
    }
127
128
    data.total_input_value = totalInputValue;
129
    data.total_output_value = tx.outputs.reduce(function(total, output) {
130
        return total + output.value;
131
    }, 0);
132
    data.total_fee = tx.fee;
133
    data.inputs = [];
134
    data.outputs = [];
135
    data.opt_in_rbf = false;
136
137
    for (var inputIdx in tx.inputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
138
        var input = tx.inputs[inputIdx];
139
        var scriptType;
140
        var inputValue;
141
        var inputTxid;
142
        var outpointIdx;
143
144
        if (input.sequence < bitcoinJS.Transaction.DEFAULT_SEQUENCE - 1) {
145
            data.opt_in_rbf = true;
146
        }
147
148
        if (data.is_coinbase && input.prev_position === -1 &&
149
            input.prev_tx_hash === "0000000000000000000000000000000000000000000000000000000000000000") {
150
            scriptType = "coinbase";
151
            inputTxid = null;
152
            inputValue = totalInputValue;
153
            outpointIdx = 0xffffffff;
154
        } else {
155
            scriptType = input.prev_type;
156
            inputValue = input.prev_value;
157
            inputTxid = input.prev_tx_hash;
158
            outpointIdx = input.prev_position;
159
        }
160
161
        data.inputs.push({
162
            index: parseInt(inputIdx, 10),
163
            output_hash: inputTxid,
164
            output_index: outpointIdx,
165
            value: inputValue,
166
            sequence: input.sequence,
167
            address: flattenAddresses(input.prev_addresses),
168
            type: convertBtccomOutputScriptType(scriptType),
169
            script_signature: input.script_hex
170
        });
171
    }
172
173
    for (var outIdx in tx.outputs) {
0 ignored issues
show
Complexity introduced by
A for in loop automatically includes the property of any prototype object, consider checking the key using hasOwnProperty.

When iterating over the keys of an object, this includes not only the keys of the object, but also keys contained in the prototype of that object. It is generally a best practice to check for these keys specifically:

var someObject;
for (var key in someObject) {
    if ( ! someObject.hasOwnProperty(key)) {
        continue; // Skip keys from the prototype.
    }

    doSomethingWith(key);
}
Loading history...
174
        var output = tx.outputs[outIdx];
175
176
        data.outputs.push({
177
            index: parseInt(outIdx, 10),
178
            value: output.value,
179
            address: flattenAddresses(output.addresses),
180
            type: convertBtccomOutputScriptType(output.type),
181
            script: prettifyAsm(output.script_asm),
182
            script_hex: output.script_hex,
183
            spent_hash: output.spent_by_tx,
184
            spent_index: output.spent_by_tx_position
185
        });
186
    }
187
188
    data.size = tx.size;
189
    data.is_double_spend = tx.is_double_spend;
190
191
    data.lock_time_timestamp = null;
192
    data.lock_time_block_height = null;
193
    if (tx.lock_time) {
194
        if (tx.lock_time < blocktrail.LOCK_TIME_TIMESTAMP_THRESHOLD) {
195
            data.lock_time_block_height = tx.lock_time;
196
        } else {
197
            data.lock_time_timestamp = tx.lock_time;
198
        }
199
    }
200
201
    // Extra fields from Btc.com
202
    data.is_sw_tx = tx.is_sw_tx;
203
    data.weight = tx.weight;
204
    data.witness_hash = tx.witness_hash;
205
    data.lock_time  = tx.lock_time;
206
    data.sigops = tx.sigops;
207
    data.version = tx.version;
208
209
    return data;
210
}
211
212
BtccomConverter.prototype.paginationParams = function(params) {
213
    if (!params) {
214
        return params;
215
    }
216
217
    if (typeof params.limit !== "undefined") {
218
        params.pagesize = params.limit;
219
        delete params.limit;
220
    }
221
222
    return params;
223
};
224
225
BtccomConverter.prototype.getUrlForBlock = function(blockHash) {
226
    return "/block/" + blockHash;
227
};
228
229
BtccomConverter.prototype.getUrlForTransaction = function(txId) {
230
    return "/tx/" + txId + "?verbose=3";
231
};
232
233
BtccomConverter.prototype.getUrlForRawTransaction = function(txId) {
234
    return "/tx/" + txId + "/raw";
235
};
236
237
BtccomConverter.prototype.getUrlForTransactions = function(txIds) {
238
    return "/tx/" + txIds.join(",") + "?verbose=3";
239
};
240
241
BtccomConverter.prototype.getUrlForBlockTransaction = function(blockHash) {
242
    return "/block/" + blockHash + "/tx?verbose=3";
243
};
244
245
BtccomConverter.prototype.getUrlForAddress = function(address) {
246
    return "/address/" + address;
247
};
248
249
BtccomConverter.prototype.getUrlForAddressTransactions = function(address) {
250
    return "/address/" + address + "/tx?verbose=3";
251
};
252
253
BtccomConverter.prototype.getUrlForAddressUnspent = function(address) {
254
    return "/address/" + address + "/unspent";
255
};
256
257
BtccomConverter.prototype.getUrlForBatchAddressUnspent = function(addresses) {
258
    return "/multi-address/" + addresses.join(",") + "/unspent";
259
};
260
261
262
BtccomConverter.prototype.getUrlForAllBlocks = function() {
263
    return "/block/list";
264
};
265
266
BtccomConverter.prototype.handleErros = function(self, data) {
267
    if (data.err_no === 0 || data.data !== null) {
268
        return data;
269
    } else {
270
        return {
271
            err_no: data.err_no,
272
            err_msg: data.err_msg,
273
            data: data.data
274
        };
275
    }
276
};
277
278
BtccomConverter.prototype.convertBlock = function(oldData) {
279
    var data = {
280
        hash: oldData.hash,
281
        version: oldData.version,
282
        height: oldData.height,
283
        block_time: utcTimestampToISODateStr(oldData.timestamp),
284
        arrival_time: utcTimestampToISODateStr(oldData.timestamp),
285
        bits: oldData.bits,
286
        nonce: oldData.nonce,
287
        merkleroot: oldData.mrkl_root,
288
        prev_block: oldData.prev_block_hash,
289
        next_block: oldData.next_block_hash,
290
        byte_size: oldData.stripped_size,
291
        difficulty: Math.floor(oldData.difficulty),
292
        transactions: oldData.tx_count,
293
        reward_block: oldData.reward_block,
294
        reward_fees: oldData.reward_fees,
295
        created_at: oldData.created_at,
296
        confirmations: oldData.confirmations,
297
        is_orphan: oldData.is_orphan,
298
        is_sw_block: oldData.is_sw_block,
299
        weight: oldData.weight,
300
        miningpool_name: oldData.miningpool_name || null,
301
        miningpool_url: oldData.miningpool_url || null,
302
        miningpool_slug: oldData.miningpool_slug || null
303
    };
304
305
    return data;
306
};
307
308
BtccomConverter.prototype.convertBlocks = function(oldData) {
309
    return {
310
        data: oldData.data.list,
311
        current_page: oldData.data.page,
312
        per_page: oldData.data.pagesize,
313
        total: oldData.data.total_count
314
    };
315
};
316
317
BtccomConverter.prototype.convertBlockTxs = function(oldData) {
318
    var list = [];
319
    oldData.data.list.forEach(function(oldTx) {
320
        var resTx = convertBtccomTxToBlocktrail(oldTx);
321
322
        list.push(resTx);
323
    });
324
325
    return {
326
        data: list,
327
        current_page: oldData.data.page,
328
        per_page: oldData.data.pagesize,
329
        total: oldData.data.total_count
330
    };
331
};
332
333
BtccomConverter.prototype.convertTx = function(oldData, rawTx) {
334
    var data = convertBtccomTxToBlocktrail(oldData.data);
335
    data.raw = rawTx;
336
    return data;
337
};
338
339
BtccomConverter.prototype.convertTxs = function(oldData) {
340
    var res = {};
341
342
    oldData.data
343
        .filter(function(tx) { return !!tx; })
344
        .forEach(function(oldTx) {
345
            var tx = convertBtccomTxToBlocktrail(oldTx);
346
            res[tx.hash] = tx;
347
        });
348
349
    return {data: res};
350
};
351
352
BtccomConverter.prototype.convertAddressTxs = function(oldData) {
353
    var data = oldData.data.list.map(function(tx) {
354
        var res = {};
355
356
        res.hash = tx.hash;
357
        res.time = utcTimestampToISODateStr(tx.block_time);
358
        res.confirmations = tx.confirmations;
359
        res.block_height = tx.block_height;
360
        res.block_hash = tx.block_hash;
361
        res.is_coinbase = tx.is_coinbase;
362
        res.total_input_value = tx.inputs_value;
363
        res.total_output_value = tx.outputs_value;
364
        res.total_fee = tx.fee;
365
366
        res.inputs = tx.inputs.map(function(input, inIdx) {
367
            return {
368
                index: inIdx,
369
                output_hash: input.prev_tx_hash,
370
                output_index: input.prev_position,
371
                value: input.prev_value,
372
                address: flattenAddresses(input.prev_addresses),
373
                type:  res.is_coinbase ? res.is_coinbase : convertBtccomOutputScriptType(input.prev_type),
374
                script_signature: input.script_hex
375
            };
376
        });
377
378
        res.outputs = tx.outputs.map(function(output, outIdx) {
379
            return {
380
                index: outIdx,
381
                value: output.value,
382
                address: flattenAddresses(output.addresses),
383
                type: convertBtccomOutputScriptType(output.type),
384
                script: prettifyAsm(output.script_asm),
385
                spent_hash: output.spent_by_tx || null,
386
                script_hex: output.script_hex,
387
                spent_index: output.spent_by_tx_position
388
            };
389
        });
390
391
        // Extra fields from Btc.com
392
        res.is_double_spend = tx.is_double_spend;
393
        res.is_sw_tx = tx.is_sw_tx;
394
        res.weight = tx.weight;
395
        res.witness_hash = tx.witness_hash;
396
        res.version = tx.version;
397
398
        return res;
399
    });
400
401
    return {
402
        data: data,
403
        current_page: oldData.data.page,
404
        per_page: oldData.data.pagesize,
405
        total: oldData.data.total_count
406
    };
407
};
408
409
BtccomConverter.prototype.convertAddress = function(oldData) {
410
    var data = {};
411
412
    data.address = oldData.data.address;
413
    data.hash160 = getBase58AddressHash160(oldData.data.address, this.network, this.useNewCashAddr).toString("hex").toUpperCase();
414
    data.balance = oldData.data.balance;
415
    data.received = oldData.data.received;
416
    data.sent = oldData.data.sent;
417
    data.transactions = oldData.data.tx_count;
418
    data.utxos = oldData.data.unspent_tx_count;
419
    data.unconfirmed_received = oldData.data.unconfirmed_received;
420
    data.unconfirmed_sent = oldData.data.unconfirmed_sent;
421
    data.unconfirmed_transactions = oldData.data.unconfirmed_tx_count;
422
    data.first_tx = oldData.data.first_tx;
423
    data.last_tx = oldData.data.last_tx;
424
425
    return data;
426
};
427
428
BtccomConverter.prototype.convertAddressUnspentOutputs = function(oldData, address) {
429
    var script = getAddressScriptInfo(address, this.network, this.useNewCashAddr);
430
    var script_hex = script.toString("hex");
431
    var script_asm = getScriptAsm(script);
432
    var type = getType(script);
433
    var data = oldData.data.list.map(function(utxo) {
434
        return {
435
            hash: utxo.tx_hash,
436
            confirmations: utxo.confirmations,
437
            value: utxo.value,
438
            index: utxo.tx_output_n,
439
            address: address,
440
            type: type,
441
            script: script_asm,
442
            script_hex: script_hex
443
        };
444
    });
445
446
    return {
447
        data: data,
448
        current_page: oldData.data.page,
449
        total: oldData.data.total_count
450
    };
451
};
452
453
BtccomConverter.prototype.convertBatchAddressUnspentOutputs = function(data) {
454
    var res = [];
455
    var total = 0;
456
457
    data.data.forEach(function(row) {
458
        var script = getAddressScriptInfo(row.address, this.network, this.useNewCashAddr);
459
        var script_hex = script.toString("hex");
460
        var script_asm = getScriptAsm(script);
461
        var type = getType(script);
462
463
        row.list.forEach(function(utxo) {
464
            total++;
465
            res.push({
466
                hash: utxo.tx_hash,
467
                index: utxo.tx_output_n,
468
                value: utxo.value,
469
                confirmations: utxo.confirmations,
470
                address: row.address,
471
                script: script_asm,
472
                script_hex: script_hex,
473
                type: type
474
            });
475
        });
476
    });
477
478
    return {
479
        data: res,
480
        current_page: null,
481
        per_page: null,
482
        total: total
483
    };
484
};
485
486
exports = module.exports = BtccomConverter;
487